#libraries needed
library(ggplot2)
library(gridExtra)
library(lme4)
1. Fitting a linear model
The file sales1.csv consists of quarterly sales volumes (in % and indexed to the time 0) of a product.
a. Plotting the data
data_path <- "/Users/mariafcadena/Documents/Master Degree/Polytechnique/Statistics in Action/Case Study 2/salesData/sales1.csv"
sales1 <- read.csv(file=data_path)
pl1 <- ggplot(sales1) +
geom_point(aes(x=time, y=y),size=3, colour="Royalblue") +
ylab("% Sales Volumes") +
xlab("time")
pl1 + ggtitle("Quarterly sales volumes")

From the plot we can see that the quarterly sales seem to have and increasing growth over the years. Since is not completely obvious the type of relation there is between the time and the sales volume we need to contruct a polynomial model that will allow us to understand this relationship.
b. Fitting a polynomial model
We are not sure from the beggining on the degree of the polynomial model we need to fit our data. Therefore, we are going to build several different models to which one of the fits better the data and gives as the best approximation for the sales volume.
Polynomial of degree 0
For this model we assume that the quarterly sales volumes don’t depend on time. Therefore, we only use the sales data as an input for our model. \[y_j=\beta_0\quad; \quad1≤j≤n\]
lm0 <- lm(y ~ 1, data = sales1)
summary(lm0)
Call:
lm(formula = y ~ 1, data = sales1)
Residuals:
Min 1Q Median 3Q Max
-12.292 -5.304 -1.355 5.055 13.299
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 111.772 1.612 69.32 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.389 on 20 degrees of freedom
\[y_j=111.78; \quad1≤j≤n\]
We only get one coefficient (\(\beta_0\)) that basically corresponds to the average of the quartely sales volume.
pl1 + geom_hline(yintercept=coef(lm0)[1],size=1, colour="Coral") + ggtitle("Polynomial Model (Degree 0)")

To understand the performance of the model we are going to check the residuals against fitted values and a normal QQ plot.
par(mfrow = c(1, 2))
plot(lm0, which=c(1,2))

We can confirm from the prediction plot and the residuals plot how this model does not fit our data. Since we are predicting for each quarter the average value, we can see from how scatter the residual and how they are not all close to zero. Hence the model does not work to fit this data.
Polynomial of degree 1
Now we are going to assume now a linear trend, defined by: \[y_j=\beta_0+\beta_1x_j+e_j \quad; \quad1≤j≤n\]
Where \(x_j\) and \(y_j\) references, respectively, the n measures of time and sales volumes.
lm1 <- lm(y ~ time, data=sales1)
summary(lm1)
Call:
lm(formula = y ~ time, data = sales1)
Residuals:
Min 1Q Median 3Q Max
-3.6994 -1.5777 -0.3596 1.6444 3.4747
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 99.99688 0.94971 105.29 < 2e-16 ***
time 0.37985 0.02643 14.37 1.17e-11 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.2 on 19 degrees of freedom
Multiple R-squared: 0.9158, Adjusted R-squared: 0.9113
F-statistic: 206.5 on 1 and 19 DF, p-value: 1.166e-11
coef(lm1)
(Intercept) time
99.9968826 0.3798515
\[y_j=99.99+0.38x_j+e_j \quad; \quad1≤j≤n\]
pl1 + geom_abline(intercept=coef(lm1)[1],slope=coef(lm1)[2],size=1, colour="lightseagreen") +
ggtitle("Polynomial Model (Degree 1")

To understand the performance of the model we are going to check the residuals against fitted values and a normal QQ plot.
par(mfrow = c(1, 2))
plot(lm1, which=c(1,2))

The residual plot suggests that the residuals are not identically distributed around 0. From the QQ plot we can infer that it may be necessary to improve the regression model since the extreme residual values are not the extreme values of a normal distribution.
Polynomial of degree 2
Given the previous results we can try describe the extreme values using a polynomial of higher degree. So we are going to try now to fit a polynomial of degree 2. \[y_j=\beta_0+\beta_1x_j+\beta_2{x_j}^2+e_j \quad; \quad1≤j≤n\]
lm2 <- lm(y ~ time + I(time^2), data=sales1)
summary(lm2)
Call:
lm(formula = y ~ time + I(time^2), data = sales1)
Residuals:
Min 1Q Median 3Q Max
-3.9334 -1.4679 -0.1228 1.5395 3.4804
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.006e+02 1.426e+00 70.521 < 2e-16 ***
time 3.209e-01 1.065e-01 3.013 0.00747 **
I(time^2) 9.511e-04 1.662e-03 0.572 0.57422
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.24 on 18 degrees of freedom
Multiple R-squared: 0.9173, Adjusted R-squared: 0.9081
F-statistic: 99.77 on 2 and 18 DF, p-value: 1.818e-10
coef(lm2)
(Intercept) time I(time^2)
1.005970e+02 3.208831e-01 9.511046e-04
\[y_j=100.59+0.32x_j+0.0095{x_j}^2+e_j \quad; \quad1≤j≤n\]
f <- function(x,c) coef(lm2)[1] +
coef(lm2)[2]*x +
coef(lm2)[3]*(x^2)
pl1 + stat_function(fun=f, colour="gold", size=1)+
ggtitle("Polynomial Model (Degree 2)")

To understand the performance of the model we are going to check the residuals against fitted values and a normal QQ plot.
par(mfrow = c(1, 2))
plot(lm2, which=c(1,2))

We can see an improvement in the residuals, since they are now closely distributed around zero
To have a better understanding of how the models perform and choose the proper one, we are going to perform the Akaike information criterion and Bayesian information criterion.
AIC(lm0,lm1,lm2)
BIC(lm0,lm1, lm2)
We are going to choose the model with lowest AIC and BIC. From the results we can see how both criteria agree for rejecting lm0 with high confidence, and both have a slight preference for lm1 over lm2. Since the interpretation for a linear model is simpler than for a second degree polynomial, we are going to choose lm1 as the model that fits our data.
c. Adding a periodic component
We ara going to try to improve our model by adding a periodic component. Since we are not sure what periodic component is the one that will help us have the best improvement to the model, we are going to try three different ones and then test which one fits better the data.
Periodic component \(cos(\frac{2\pi time}{12})\)
lm_p1 <- lm(data = sales1, y ~ time + I(cos(2*pi*time/12)))
f <- function(x,c) coef(lm_p1)[1] +
coef(lm_p1)[2]*x +
coef(lm_p1)[3]*cos(2*pi*x/12)
pl1 + stat_function(fun=f, colour="gold", size=1)+
ggtitle("Polynomial Model with periodic componet cos()")

Periodic component \(sin(\frac{2\pi time}{12})\)
lm_p2 <- lm(data = sales1, y ~ time + I(sin(2*pi*time/12)))
f <- function(x,c) coef(lm_p2)[1] +
coef(lm_p2)[2]*x +
coef(lm_p2)[3]*cos(2*pi*x/12)
pl1 + stat_function(fun=f, colour="lightblue", size=1)+
ggtitle("Polynomial Model with periodic componet sin()")

Periodic component \(cos(\frac{2\pi time}{12}) + sin(\frac{2\pi time}{12})\)
lm_p3 <- lm(data = sales1, y ~ time + I(cos(2*pi*time/12)+sin(2*pi*time/12)))
f <- function(x,c) coef(lm_p3)[1] +
coef(lm_p3)[2]*x +
coef(lm_p3)[3]*cos(2*pi*x/12)
pl1 + stat_function(fun=f, colour="lightgreen", size=1)+
ggtitle("Polynomial Model with periodic componet cos() + sin()")

We are going to perform now the AIC and BIC test in order to check which model fits better our data.
AIC(lm_p1,lm_p2,lm_p3)
BIC(lm_p1,lm_p2,lm_p3)
We can see how is suggested by AIC and BIC that lm_3 is the model that best describes the behaviour of the quarterly sales volume.
coef(lm_p3)
(Intercept) time
99.7966320 0.3826308
I(cos(2 * pi * time/12) + sin(2 * pi * time/12))
1.7539826
Therefore, the model that describes our data is define as following: \[y_j=99.69+0.38x_j+1.75*\bigg[cos\bigg(\frac{2 \pi x_j}{12}\bigg)+sin\bigg(\frac{2 \pi x_j}{12}\bigg)\bigg]+e_j \quad; \quad1≤j≤n\]
d. Validating the results
f <- function(x,c) coef(lm_p3)[1] +
coef(lm_p3)[2]*x +
coef(lm_p3)[3]*(cos(2*pi*x/12)+sin(2*pi*x/12))
pl1 + stat_function(fun=f, colour="coral", size=1) + ggtitle("Quarterly Volume Sales Prediction vs Real values")

par(mfrow = c(1, 1))
plot(lm_p3, which=c(1,1))

We can see how the residuals we shift the distribution of the residuals from the obe we obtained for the regression model without the periodic component. We still get a fairly distribution of the residuals around zero. We can agreee that this model fits better our data that the one we defined before.
e. Adding a constraint on the intercept
We want the predicted sales volume to be equal to 100 at time 0. Therefore we redefined our model as:
lm_periodic2 <- lm(data = sales1, I(y-100) ~ 0 + time+ I(cos(2*pi*time/12)+sin(2*pi*time/12)))
summary(lm_periodic2)
Call:
lm(formula = I(y - 100) ~ 0 + time + I(cos(2 * pi * time/12) +
sin(2 * pi * time/12)), data = sales1)
Residuals:
Min 1Q Median 3Q Max
-1.94138 -0.89474 -0.03139 0.83742 2.17444
Coefficients:
Estimate Std. Error t value Pr(>|t|)
time 0.37775 0.00701 53.891 < 2e-16 ***
I(cos(2 * pi * time/12) + sin(2 * pi * time/12)) 1.74827 0.24682 7.083 9.73e-07 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.153 on 19 degrees of freedom
Multiple R-squared: 0.9937, Adjusted R-squared: 0.993
F-statistic: 1495 on 2 and 19 DF, p-value: < 2.2e-16
f <- function(x,c) 100 + coef(lm_periodic2)[1] * x +
coef(lm_periodic2)[2] * (cos(2*pi*x/12)+sin(2*pi*x/12))
pl1 + stat_function(fun=f, colour="lightcoral", size=1)

par(mfrow = c(1, 1))
plot(lm_periodic2, which=c(1,1))

Adding the constraint over the intercept has a sligth impact on the fitting of the model. Which makes sense since we are not trying the model to fit the intercept to the data available, instead we are forcing it to a defualt value of 100.
2. Fitting a linear mixed effects model Now we hve the data of quarterly sales volumes of 30 different products.
a. Plotting the data
sales30 <- read.csv("/Users/mariafcadena/Documents/Master Degree/Polytechnique/Statistics in Action/Case Study 2/salesData/sales30.csv")
pl2 <-ggplot(sales30) + geom_point(aes(x=time, y=y),size=1, colour="Royalblue") + ggtitle("Quarterly sales volumes")
print(pl2)

pl3 <- ggplot(sales30) +
geom_point(aes(x=time, y=y),size=1, colour="Royalblue") +
ggtitle("Quarterly sales volumes") +
facet_wrap(~id, ncol=6)
print(pl3)

b. Fitting a model to the first product
We are going to fit the model used previously for fitting the first series to this data.
lm_periodic3 <- lm(data = subset(sales30, id==1), y ~ time + I(cos(2*pi*time/12)+sin(2*pi*time/12)))
summary(lm_periodic3)
Call:
lm(formula = y ~ time + I(cos(2 * pi * time/12) + sin(2 * pi *
time/12)), data = subset(sales30, id == 1))
Residuals:
Min 1Q Median 3Q Max
-1.86773 -0.74963 0.07146 0.90215 2.12213
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 99.79663 0.50996 195.697 < 2e-16 ***
time 0.38263 0.01418 26.992 5.16e-16 ***
I(cos(2 * pi * time/12) + sin(2 * pi * time/12)) 1.75398 0.25288 6.936 1.76e-06 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.18 on 18 degrees of freedom
Multiple R-squared: 0.9771, Adjusted R-squared: 0.9745
F-statistic: 383.4 on 2 and 18 DF, p-value: 1.758e-15
f <- function(x,c) coef(lm_periodic3)[1] +
coef(lm_periodic3)[2]*x +
coef(lm_periodic3)[3]*(cos(2*pi*x/12)+sin(2*pi*x/12))
pl3 <-ggplot(subset(sales30,id==1)) + geom_point(aes(x=time, y=y),size=2, colour="Royalblue") + ggtitle("Quarterly sales volumes first series")
pl3 + stat_function(fun=f, colour="lightcoral", size=1)

par(mfrow = c(1, 1))
plot(lm_periodic3, which=c(1,1))

We can see how we get more or less the same results as the ones we obtained with the previous data. So we can agree that this models suits us for predicting the sales volume of one product on the data set.
c. Fitting a mixed effect model
We have been able to create a model that describes pretty accurately the prediction of the sales volumes of one product. Now if we want to construct a model for discribing the overall behaviour of the sales volume for the different products its important to take into account the effect that each product may have over the prediciont. From the start is not clear to know over which component of the model the type of product will have and impact. Therefore we are going to define several different models assuming all the scenarios where factors could depend on the product. Then we are going to test and choose the model that best fits our data.
Model 1: Nothing depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+e_{ij}\]
Model 2: The value of the intercept (sales volume at time 0) depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i0}+e_{ij}\]
Model 3: The growth rate per quarter depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i1}*x_{ij}+e_{ij}\]
Model 4: The intercept and the growth rate per quarter depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i0}+\eta_{i1}*x_{ij}+e_{ij}\]
Model 5: The periodic component depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+e_{ij}\]
Model 6: The intercept and the periodic component depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i0}+\eta_{i2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+e_{ij}\]
Model 7: The growth rate per quarter and periodic component depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i1}*x_{ij}+\eta_{i2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+e_{ij}\]
Model 8: Evereything depends on the product: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i0}+\eta_{i1}*x_{ij}+\eta_{i2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+e_{ij}\]
m1 <- lm(y ~ time + I(cos(2*pi*time/12) + sin(2*pi*time/12)), data=sales30)
m2 <- lmer(y ~ time + I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (1|id), data=sales30)
m3 <- lmer(y ~ time + I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (-1 + time|id) , data=sales30)
m4 <- lmer(y ~ 1 + time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (time|id) , data=sales30)
m5 <- lmer(y ~ time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (-1+I(cos(2*pi*time/12) + sin(2*pi*time/12))|id) , data=sales30)
m6 <- lmer(y ~ 1 +time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (I(cos(2*pi*time/12) + sin(2*pi*time/12))|id) , data=sales30)
m7 <- lmer(y ~ time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (-1+time+I(cos(2*pi*time/12) + sin(2*pi*time/12))|id) , data=sales30)
m8 <- lmer(y ~ 1+ time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (time+I(cos(2*pi*time/12) + sin(2*pi*time/12))|id) , data=sales30)
BIC(m1,m2,m3,m4, m5,m6,m7, m8)
The BIC suggest then that the model that best fit our data and explains the effect each product has over the general model is model 7. This models suggest that each products introduce a random effect in the growth rate and also on the periodic component but the intercept remains the same for all the products.
m7
Linear mixed model fit by REML ['lmerMod']
Formula: y ~ time + I(cos(2 * pi * time/12) + sin(2 * pi * time/12)) +
(-1 + time + I(cos(2 * pi * time/12) + sin(2 * pi * time/12)) | id)
Data: sales30
REML criterion at convergence: 2788.325
Random effects:
Groups Name Std.Dev. Corr
id time 0.1376
I(cos(2 * pi * time/12) + sin(2 * pi * time/12)) 1.3184 -0.35
Residual 1.8468
Number of obs: 630, groups: id, 30
Fixed Effects:
(Intercept) time
100.1225 0.2155
I(cos(2 * pi * time/12) + sin(2 * pi * time/12))
1.0666
Therefore the mixed effect model that describes our data is defined as following: \[y_{ij}=100.12+0.21*x_{ij}+1.06*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i1}*x_{ij}+\eta_{i2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+e_{ij}\]
d. Plotting the predicted values
We are going to plot the data with the predicted sales given by our final model.
prediction <- fitted(m7)
ggplot(data=sales30) + geom_point(aes(x=time,y=y), color="lightseagreen", size=1) +
geom_line(aes(x=time,y=prediction), color="Royalblue") + facet_wrap(~id, ncol=6)

qqnorm(residuals(m7))

As we can see the plots above, our model fits pretty well every one of the 30 products since the quantile plot does not raise any significant concern with normality of the residuals.
d. Adding a constraint over the intercept
We are going to set the predicted sales volume of all products equal to 100 at time 0.
constraint_model <- lmer(I(y-100) ~ 0 +time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (-1+time+I(cos(2*pi*time/12) + sin(2*pi*time/12))|id), data = sales30)
f<-fitted(constraint_model)
ggplot(data=sales30) + geom_point(aes(x=time,y=y), color="lightseagreen", size=1) +
geom_line(aes(x=time,y=f+100), color="Royalblue") + facet_wrap(~id, ncol=6)

qqnorm(residuals(constraint_model))

Once again we see how adding the constraint, still gets us a well fitted model and as we can see in the quantile plot it does not have a negative impact on the effectiveness of our model. 2. Individual prediction Now we are going to see how well does our model perform for predicting the sales volume for a new product.
data_path <- "/Users/mariafcadena/Documents/Master Degree/Polytechnique/Statistics in Action/Case Study 2/salesData/salesNew.csv"
salesNew <- read.csv(file=data_path)
a. Prediction without any data
Without any information on how are the sales for this new product we will assume that we can predict the sales with the fixed parts of the model we previously defined.
Given that our model is: \[y_{ij}=\beta_{0}+\beta_{1}*x_{ij}+\beta_{2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+\eta_{i1}*x_{ij}+\eta_{i2}*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]+e_{ij}\] We will assume that \(\eta_{i0}\), \(\eta_{i1}\), and \(\eta_{i2}\) are zero since we dont have anydata to define the random effect this product will have over the population values.
Therefore our equation for prediction the values of this product will be: \[y_{ij}=100.12+0.21*x_{ij}+1.06*\bigg[cos\bigg(\frac{2\pi x_{ij}}{12}\bigg)+sin\bigg(\frac{2\pi x_{ij}}{12}\bigg)\bigg]\] (Intercept) time
I(cos(2 * pi * time/12) + sin(2 * pi * time/12))
f <- function(x,c) 100.1225 +
0.2155*x +
1.0666*(cos(2*pi*x/12)+sin(2*pi*x/12))
pl4 <- ggplot(salesNew) +
geom_point(aes(x=time, y=y),size=3, colour="Royalblue") +
ylab("% Sales Volumes") +
xlab("time")
print(pl4 + ggtitle("Quarterly sales volumes new product") + stat_function(fun=f, colour="lightcoral", size=1))

As we can see, the fixed part of our model gives us an approximation of the sales volume for the product. But its clear that there is a need on having some data from the previous sales behaviour of product, to fit or train our model to predict more accurately the sales for this new product. Or in other words to understand the random effect that this product has on the “population” growth and periodical component.
b. Prediction with one data point for the new product
To adjust the model we are going to fit again the mixed effect model with the data from the new product. Since we only have data for one period (time=1), then we are going to assume the population values for the other quarters and train the model with this data.
New_Data<-sales30
New_Product<-as.data.frame(cbind(matrix(31, ncol = 1, nrow = nrow(salesNew)), salesNew$time))
f <- function(x,c) 100.1225 +
0.2155*x +
1.0666*(cos(2*pi*x/12)+sin(2*pi*x/12))
New_Product<-cbind(New_Product, lapply(New_Product[2], f))
colnames(New_Product) <- c("id", "time","y")
#We create a new dataframe with the new product, the values that we used for the sales volume (y) are the ones predicted by the fixed
New_Data<-rbind(New_Data, New_Product)
#We update the value of time 1 for the new product with the known value
time1<-salesNew$y[salesNew$time == 1 ]
New_Data$y[New_Data$time==1 & New_Data$id==31]<-time1
#Now we fit again the mixed effect model with this new data
New_model_d1<- lmer(y ~ time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (-1+time+I(cos(2*pi*time/12) + sin(2*pi*time/12))|id) , data=New_Data)
To validate how the new model fits the new product, we are going to obtain the betas or coefficients from the mixed effect model for the new product (id=31) and then contruct the function to predict the sales volume. With this information we are going to plot our prediction vs the actual values to see how well the model performs.
betas<-cbind(coef(New_model_d1)$id, matrix(c(1:31),ncol=1, nrow=31))
colnames(betas) <- c("b_0", "b_1","b_2", "id")
f_d1 <- function(x,c) betas$b_0[betas$id==31] +
betas$b_1[betas$id==31]*x +
betas$b_2[betas$id==31]*(cos(2*pi*x/12)+sin(2*pi*x/12))
print(pl4 + ggtitle("Quarterly sales volumes new product") + stat_function(fun=f_d1, colour="lightcoral", size=1))

And compute the residual sum of squares to understand how good the model performs.
Final_Data <- as.data.frame((salesNew[2]-lapply(salesNew[1], f))^2)
PredictionError <- sum(Final_Data$y)
PredictionError
[1] 1250.952
We can see that still the model does not gather enough information to correctly defined the random effect that the new product has over the fixed model
c. Prediction with increasing data for the new product
Now we are going to perform the same computation that we did before, but now adding at a time one known value of the sales volume. To see how adding information to the model helps as improve the fitting of the model.
New_Data<-sales30
New_Product<-as.data.frame(cbind(matrix(31, ncol = 1, nrow = nrow(salesNew)), salesNew$time))
f <- function(x,c) 100.1225 +
0.2155*x +
1.0666*(cos(2*pi*x/12)+sin(2*pi*x/12))
New_Product<-cbind(New_Product, lapply(New_Product[2], f))
colnames(New_Product) <- c("id", "time","y")
#We create a new dataframe with the new product, the values that we used for the sales volume (y) are the ones predicted by the fixed
New_Data<-rbind(New_Data, New_Product)
iter=0
for (i in salesNew$time)
{
iter=iter+1
#We update the value of y with then known sales valume for the i quarter
sales_iquarter<-salesNew$y[salesNew$time == i ]
New_Data$y[New_Data$time==i & New_Data$id==31]<-sales_iquarter
#We fit the model to the new data
New_model_dt<- lmer(y ~ time+ I(cos(2*pi*time/12) + sin(2*pi*time/12)) + (-1+time+I(cos(2*pi*time/12) + sin(2*pi*time/12))|id) , data=New_Data)
#we obtain the coefficients and re-construct the model for the new product (id=31)
betas<-cbind(coef(New_model_dt)$id, matrix(c(1:31),ncol=1, nrow=31))
colnames(betas) <- c("b_0", "b_1","b_2", "id")
f <- function(x,c) betas$b_0[betas$id==31] +
betas$b_1[betas$id==31]*x +
betas$b_2[betas$id==31]*(cos(2*pi*x/12)+sin(2*pi*x/12))
#We compute the new prediction for the sales volume and store in a dataframe the results of our iteration
if (i==1){
Results<-as.data.frame(cbind(matrix(toString(iter), ncol = 1, nrow = nrow(salesNew)),salesNew$time,salesNew$y, as.data.frame(lapply(salesNew[1], f))))
colnames(Results)<-c("iteration","time","y","prediction")
} else{
temp<-as.data.frame(cbind(matrix(toString(iter), ncol = 1, nrow = nrow(salesNew)),salesNew$time,salesNew$y, as.data.frame(lapply(salesNew[1], f))))
colnames(temp)<-c("iteration","time","y","prediction")
Results<-rbind(Results,temp)
}
}
Having the results for each iteration of adding known values to fit the model, we can now plot the predicted values per iteration against the actual values.
pl5 <- ggplot(Results) +
geom_point(aes(x=time, y=y),size=3, colour="Royalblue") +
geom_line(aes(x=time, y=prediction,col=iteration)) +
ylab("% Sales Volumes") +
xlab("time")
pl5 + ggtitle("Predicted Values per Iteration vs Actual Values")

We also comput the sum of residual errors to be able to compare the results of each model
Results["SR"]<-as.data.frame((Results$y-Results$prediction)^2)
SSR<-aggregate(Results$SR, by=list(iteration=Results$iteration), FUN=sum)
ggplot(SSR) + geom_point(aes(x=iteration, y=x),size=2, colour="red") + ggtitle("Convergence of SSR per Iteration")

We see how having more and more information of our new product help the model to have a better fit of the data and therefore have lower residual error
We can plot the final fit of the mixed effect model with all the data available:
f<-fitted(New_model)
ggplot(data=New_Data) + geom_point(aes(x=time,y=y), color="lightseagreen", size=1) +
geom_line(aes(x=time,y=f), color="Royalblue") + facet_wrap(~id, ncol=6)

We can see how the model manages to gather the effects of the new product and at the same time does not damages the predictions for the other products.
LS0tCnRpdGxlOiAiQ0FERU5BIE1hcmlhLCBDYXNlIFN0dWR5IDIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KCmBgYHtyfQojbGlicmFyaWVzIG5lZWRlZApsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGxtZTQpCmBgYAo8L2I+CjwvYj4KPGZvbnQgc2l6ZT0iNSIgY29sb3I9InNlYWdyZWVuIj48Yj4KKjEuIEZpdHRpbmcgYSBsaW5lYXIgbW9kZWwqCjwvYj48L2ZvbnQ+PGJyLz4KPC9iPgpUaGUgZmlsZSBzYWxlczEuY3N2IGNvbnNpc3RzIG9mIHF1YXJ0ZXJseSBzYWxlcyB2b2x1bWVzIChpbiAlIGFuZCBpbmRleGVkIHRvIHRoZSB0aW1lIDApIG9mIGEgcHJvZHVjdC48YnIvPgo8YnIvPgo8Zm9udCBzaXplPSIzIiBjb2xvcj0iTWVkaXVtU2VhR3JlZW4iPgoqYS4gUGxvdHRpbmcgdGhlIGRhdGEqCjwvZm9udD48YnIvPgpgYGB7cn0KZGF0YV9wYXRoIDwtICIvVXNlcnMvbWFyaWFmY2FkZW5hL0RvY3VtZW50cy9NYXN0ZXIgRGVncmVlL1BvbHl0ZWNobmlxdWUvU3RhdGlzdGljcyBpbiBBY3Rpb24vQ2FzZSBTdHVkeSAyL3NhbGVzRGF0YS9zYWxlczEuY3N2IgpzYWxlczEgICAgPC0gcmVhZC5jc3YoZmlsZT1kYXRhX3BhdGgpCnBsMSAgICAgICA8LSBnZ3Bsb3Qoc2FsZXMxKSArIAogICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoeD10aW1lLCB5PXkpLHNpemU9MywgY29sb3VyPSJSb3lhbGJsdWUiKSArCiAgICAgICAgICAgICB5bGFiKCIlIFNhbGVzIFZvbHVtZXMiKSArCiAgICAgICAgICAgICB4bGFiKCJ0aW1lIikKcGwxICsgZ2d0aXRsZSgiUXVhcnRlcmx5IHNhbGVzIHZvbHVtZXMiKQpgYGAKPHAgYWxpZ249Imp1c3RpZnkiPgpGcm9tIHRoZSBwbG90IHdlIGNhbiBzZWUgdGhhdCB0aGUgcXVhcnRlcmx5IHNhbGVzIHNlZW0gdG8gaGF2ZSBhbmQgaW5jcmVhc2luZyBncm93dGggb3ZlciB0aGUgeWVhcnMuIFNpbmNlIGlzIG5vdCBjb21wbGV0ZWx5IG9idmlvdXMgdGhlIHR5cGUgb2YgcmVsYXRpb24gdGhlcmUgaXMgYmV0d2VlbiB0aGUgdGltZSBhbmQgdGhlIHNhbGVzIHZvbHVtZSB3ZSBuZWVkIHRvIGNvbnRydWN0IGEgcG9seW5vbWlhbCBtb2RlbCB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gdW5kZXJzdGFuZCB0aGlzIHJlbGF0aW9uc2hpcC48YnIvPgo8YnIvPgo8Zm9udCBzaXplPSIzIiBjb2xvcj0iTWVkaXVtU2VhR3JlZW4iPgoqYi4gRml0dGluZyBhIHBvbHlub21pYWwgbW9kZWwqCjwvZm9udD48YnIvPgpXZSBhcmUgbm90IHN1cmUgZnJvbSB0aGUgYmVnZ2luaW5nIG9uIHRoZSBkZWdyZWUgb2YgdGhlIHBvbHlub21pYWwgbW9kZWwgd2UgbmVlZCB0byBmaXQgb3VyIGRhdGEuIFRoZXJlZm9yZSwgd2UgYXJlIGdvaW5nIHRvIGJ1aWxkIHNldmVyYWwgZGlmZmVyZW50IG1vZGVscyB0byB3aGljaCBvbmUgb2YgdGhlIGZpdHMgYmV0dGVyIHRoZSBkYXRhIGFuZCBnaXZlcyBhcyB0aGUgYmVzdCBhcHByb3hpbWF0aW9uIGZvciB0aGUgc2FsZXMgdm9sdW1lLjxici8+Cjxici8+CjxiPipQb2x5bm9taWFsIG9mIGRlZ3JlZSAwKjwvYj4KPGJyLz4KRm9yIHRoaXMgbW9kZWwgd2UgYXNzdW1lIHRoYXQgdGhlIHF1YXJ0ZXJseSBzYWxlcyB2b2x1bWVzIGRvbid0IGRlcGVuZCBvbiB0aW1lLiBUaGVyZWZvcmUsIHdlIG9ubHkgdXNlIHRoZSBzYWxlcyBkYXRhIGFzIGFuIGlucHV0IGZvciBvdXIgbW9kZWwuCiQkeV9qPVxiZXRhXzBccXVhZDsgXHF1YWQx4omkauKJpG4kJApgYGB7cn0KbG0wIDwtIGxtKHkgfiAxLCBkYXRhID0gc2FsZXMxKQpzdW1tYXJ5KGxtMCkKYGBgCiQkeV9qPTExMS43ODsgXHF1YWQx4omkauKJpG4kJAo8YnIvPldlIG9ubHkgZ2V0IG9uZSBjb2VmZmljaWVudCAoJFxiZXRhXzAkKSB0aGF0IGJhc2ljYWxseSBjb3JyZXNwb25kcyB0byB0aGUgYXZlcmFnZSBvZiB0aGUgcXVhcnRlbHkgc2FsZXMgdm9sdW1lLjxici8+CmBgYHtyfQpwbDEgICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PWNvZWYobG0wKVsxXSxzaXplPTEsIGNvbG91cj0iQ29yYWwiKSArIGdndGl0bGUoIlBvbHlub21pYWwgTW9kZWwgKERlZ3JlZSAwKSIpCmBgYAo8YnIvPiBUbyB1bmRlcnN0YW5kIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbW9kZWwgd2UgYXJlIGdvaW5nIHRvIGNoZWNrIHRoZSByZXNpZHVhbHMgYWdhaW5zdCBmaXR0ZWQgdmFsdWVzIGFuZCBhIG5vcm1hbCBRUSBwbG90LgpgYGB7cn0KcGFyKG1mcm93ID0gYygxLCAyKSkKcGxvdChsbTAsIHdoaWNoPWMoMSwyKSkKYGBgCjxici8+V2UgY2FuIGNvbmZpcm0gZnJvbSB0aGUgcHJlZGljdGlvbiBwbG90IGFuZCB0aGUgcmVzaWR1YWxzIHBsb3QgaG93IHRoaXMgbW9kZWwgZG9lcyBub3QgZml0IG91ciBkYXRhLiBTaW5jZSB3ZSBhcmUgcHJlZGljdGluZyBmb3IgZWFjaCBxdWFydGVyIHRoZSBhdmVyYWdlIHZhbHVlLCB3ZSBjYW4gc2VlIGZyb20gaG93IHNjYXR0ZXIgdGhlIHJlc2lkdWFsIGFuZCBob3cgdGhleSBhcmUgbm90IGFsbCBjbG9zZSB0byB6ZXJvLiBIZW5jZSB0aGUgbW9kZWwgZG9lcyBub3Qgd29yayB0byBmaXQgdGhpcyBkYXRhLjxici8+Cjxici8+CjxiPipQb2x5bm9taWFsIG9mIGRlZ3JlZSAxKjwvYj4KPGJyLz4KTm93IHdlIGFyZSBnb2luZyB0byBhc3N1bWUgbm93IGEgbGluZWFyIHRyZW5kLCBkZWZpbmVkIGJ5OgokJHlfaj1cYmV0YV8wK1xiZXRhXzF4X2orZV9qIFxxdWFkOyBccXVhZDHiiaRq4omkbiQkCjxici8+V2hlcmUgJHhfaiQgYW5kICR5X2okIHJlZmVyZW5jZXMsIHJlc3BlY3RpdmVseSwgdGhlIG4gbWVhc3VyZXMgb2YgdGltZSBhbmQgc2FsZXMgdm9sdW1lcy48YnIvPgpgYGB7cn0KbG0xIDwtIGxtKHkgfiB0aW1lLCBkYXRhPXNhbGVzMSkKc3VtbWFyeShsbTEpCmBgYApgYGB7cn0KY29lZihsbTEpCmBgYAokJHlfaj05OS45OSswLjM4eF9qK2VfaiBccXVhZDsgXHF1YWQx4omkauKJpG4kJApgYGB7cn0KcGwxICArIGdlb21fYWJsaW5lKGludGVyY2VwdD1jb2VmKGxtMSlbMV0sc2xvcGU9Y29lZihsbTEpWzJdLHNpemU9MSwgY29sb3VyPSJsaWdodHNlYWdyZWVuIikgKwogICAgICAgZ2d0aXRsZSgiUG9seW5vbWlhbCBNb2RlbCAoRGVncmVlIDEiKQpgYGAKPGJyLz4gVG8gdW5kZXJzdGFuZCB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIG1vZGVsIHdlIGFyZSBnb2luZyB0byBjaGVjayB0aGUgcmVzaWR1YWxzIGFnYWluc3QgZml0dGVkIHZhbHVlcyBhbmQgYSBub3JtYWwgUVEgcGxvdC4KYGBge3J9CnBhcihtZnJvdyA9IGMoMSwgMikpCnBsb3QobG0xLCB3aGljaD1jKDEsMikpCmBgYAo8YnIvPlRoZSByZXNpZHVhbCBwbG90IHN1Z2dlc3RzIHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgbm90IGlkZW50aWNhbGx5IGRpc3RyaWJ1dGVkIGFyb3VuZCAwLiBGcm9tIHRoZSBRUSBwbG90IHdlIGNhbiBpbmZlciB0aGF0IGl0IG1heSBiZSAgbmVjZXNzYXJ5IHRvIGltcHJvdmUgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgc2luY2UgdGhlIGV4dHJlbWUgcmVzaWR1YWwgdmFsdWVzIGFyZSBub3QgdGhlIGV4dHJlbWUgdmFsdWVzIG9mIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi48YnIvPgo8YnIvPgo8Yj4qUG9seW5vbWlhbCBvZiBkZWdyZWUgMio8L2I+Cjxici8+CkdpdmVuIHRoZSBwcmV2aW91cyByZXN1bHRzIHdlIGNhbiB0cnkgZGVzY3JpYmUgdGhlIGV4dHJlbWUgdmFsdWVzIHVzaW5nIGEgcG9seW5vbWlhbCBvZiBoaWdoZXIgZGVncmVlLiBTbyB3ZSBhcmUgZ29pbmcgdG8gdHJ5IG5vdyB0byBmaXQgYSBwb2x5bm9taWFsIG9mIGRlZ3JlZSAyLgokJHlfaj1cYmV0YV8wK1xiZXRhXzF4X2orXGJldGFfMnt4X2p9XjIrZV9qIFxxdWFkOyBccXVhZDHiiaRq4omkbiQkCjxici8+CmBgYHtyfQpsbTIgPC0gbG0oeSB+IHRpbWUgKyBJKHRpbWVeMiksIGRhdGE9c2FsZXMxKQpzdW1tYXJ5KGxtMikKYGBgCmBgYHtyfQpjb2VmKGxtMikKYGBgCiQkeV9qPTEwMC41OSswLjMyeF9qKzAuMDA5NXt4X2p9XjIrZV9qIFxxdWFkOyBccXVhZDHiiaRq4omkbiQkCmBgYHtyfQpmIDwtIGZ1bmN0aW9uKHgsYykgY29lZihsbTIpWzFdICsgCiAgICAgICAgICAgICAgICAgICBjb2VmKGxtMilbMl0qeCArCiAgICAgICAgICAgICAgICAgICBjb2VmKGxtMilbM10qKHheMikKCnBsMSAgKyBzdGF0X2Z1bmN0aW9uKGZ1bj1mLCBjb2xvdXI9ImdvbGQiLCBzaXplPTEpKyAKICAgICAgIGdndGl0bGUoIlBvbHlub21pYWwgTW9kZWwgKERlZ3JlZSAyKSIpCmBgYAo8YnIvPiBUbyB1bmRlcnN0YW5kIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbW9kZWwgd2UgYXJlIGdvaW5nIHRvIGNoZWNrIHRoZSByZXNpZHVhbHMgYWdhaW5zdCBmaXR0ZWQgdmFsdWVzIGFuZCBhIG5vcm1hbCBRUSBwbG90LgpgYGB7cn0KcGFyKG1mcm93ID0gYygxLCAyKSkKcGxvdChsbTIsIHdoaWNoPWMoMSwyKSkKYGBgCjxici8+IFdlIGNhbiBzZWUgYW4gaW1wcm92ZW1lbnQgaW4gdGhlIHJlc2lkdWFscywgc2luY2UgdGhleSBhcmUgbm93IGNsb3NlbHkgZGlzdHJpYnV0ZWQgYXJvdW5kIHplcm88YnIvPgo8YnIvPiBUbyBoYXZlIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgaG93IHRoZSBtb2RlbHMgcGVyZm9ybSBhbmQgY2hvb3NlIHRoZSBwcm9wZXIgb25lLCB3ZSBhcmUgZ29pbmcgdG8gcGVyZm9ybSB0aGUgQWthaWtlIGluZm9ybWF0aW9uIGNyaXRlcmlvbiBhbmQgQmF5ZXNpYW4gaW5mb3JtYXRpb24gY3JpdGVyaW9uLgpgYGB7cn0KQUlDKGxtMCxsbTEsbG0yKQpgYGAKCmBgYHtyfQpCSUMobG0wLGxtMSwgbG0yKQpgYGAKPGJyLz5XZSBhcmUgZ29pbmcgdG8gY2hvb3NlIHRoZSBtb2RlbCB3aXRoIGxvd2VzdCBBSUMgYW5kIEJJQy4gRnJvbSB0aGUgcmVzdWx0cyB3ZSBjYW4gc2VlIGhvdyBib3RoIGNyaXRlcmlhIGFncmVlIGZvciByZWplY3RpbmcgbG0wIHdpdGggaGlnaCBjb25maWRlbmNlLCBhbmQgYm90aCBoYXZlIGEgc2xpZ2h0IHByZWZlcmVuY2UgZm9yIGxtMSBvdmVyIGxtMi4gU2luY2UgdGhlIGludGVycHJldGF0aW9uIGZvciBhIGxpbmVhciBtb2RlbCBpcyBzaW1wbGVyIHRoYW4gZm9yIGEgc2Vjb25kIGRlZ3JlZSBwb2x5bm9taWFsLCB3ZSBhcmUgZ29pbmcgdG8gY2hvb3NlIGxtMSBhcyB0aGUgbW9kZWwgdGhhdCBmaXRzIG91ciBkYXRhLjxici8+Cjxici8+Cjxmb250IHNpemU9IjMiIGNvbG9yPSJNZWRpdW1TZWFHcmVlbiI+CipjLiBBZGRpbmcgYSBwZXJpb2RpYyBjb21wb25lbnQqCjwvZm9udD48YnIvPgpXZSBhcmEgZ29pbmcgdG8gdHJ5IHRvIGltcHJvdmUgb3VyIG1vZGVsIGJ5IGFkZGluZyBhIHBlcmlvZGljIGNvbXBvbmVudC4gU2luY2Ugd2UgYXJlIG5vdCBzdXJlIHdoYXQgcGVyaW9kaWMgY29tcG9uZW50IGlzIHRoZSBvbmUgdGhhdCB3aWxsIGhlbHAgdXMgaGF2ZSB0aGUgYmVzdCBpbXByb3ZlbWVudCB0byB0aGUgbW9kZWwsIHdlIGFyZSBnb2luZyB0byB0cnkgdGhyZWUgZGlmZmVyZW50IG9uZXMgYW5kIHRoZW4gdGVzdCB3aGljaCBvbmUgZml0cyBiZXR0ZXIgdGhlIGRhdGEuPGJyLz4KPGI+KlBlcmlvZGljIGNvbXBvbmVudCogJGNvcyhcZnJhY3syXHBpIHRpbWV9ezEyfSkkPC9iPgpgYGB7cn0KbG1fcDEgPC0gbG0oZGF0YSA9IHNhbGVzMSwgeSB+IHRpbWUgKyBJKGNvcygyKnBpKnRpbWUvMTIpKSkKZiA8LSBmdW5jdGlvbih4LGMpIGNvZWYobG1fcDEpWzFdICsgCiAgICAgICAgICAgICAgICAgICBjb2VmKGxtX3AxKVsyXSp4ICsKICAgICAgICAgICAgICAgICAgIGNvZWYobG1fcDEpWzNdKmNvcygyKnBpKngvMTIpCgpwbDEgICsgc3RhdF9mdW5jdGlvbihmdW49ZiwgY29sb3VyPSJnb2xkIiwgc2l6ZT0xKSsgCiAgICAgICBnZ3RpdGxlKCJQb2x5bm9taWFsIE1vZGVsIHdpdGggcGVyaW9kaWMgY29tcG9uZXQgY29zKCkiKQpgYGAKPGI+KlBlcmlvZGljIGNvbXBvbmVudCogJHNpbihcZnJhY3syXHBpIHRpbWV9ezEyfSkkPC9iPgpgYGB7cn0KbG1fcDIgPC0gbG0oZGF0YSA9IHNhbGVzMSwgeSB+IHRpbWUgKyBJKHNpbigyKnBpKnRpbWUvMTIpKSkKZiA8LSBmdW5jdGlvbih4LGMpIGNvZWYobG1fcDIpWzFdICsgCiAgICAgICAgICAgICAgICAgICBjb2VmKGxtX3AyKVsyXSp4ICsKICAgICAgICAgICAgICAgICAgIGNvZWYobG1fcDIpWzNdKmNvcygyKnBpKngvMTIpCgpwbDEgICsgc3RhdF9mdW5jdGlvbihmdW49ZiwgY29sb3VyPSJsaWdodGJsdWUiLCBzaXplPTEpKyAKICAgICAgIGdndGl0bGUoIlBvbHlub21pYWwgTW9kZWwgd2l0aCBwZXJpb2RpYyBjb21wb25ldCBzaW4oKSIpCmBgYAo8Yj4qUGVyaW9kaWMgY29tcG9uZW50KiAkY29zKFxmcmFjezJccGkgdGltZX17MTJ9KSArIHNpbihcZnJhY3syXHBpIHRpbWV9ezEyfSkkPC9iPgpgYGB7cn0KbG1fcDMgPC0gbG0oZGF0YSA9IHNhbGVzMSwgeSB+IHRpbWUgKyBJKGNvcygyKnBpKnRpbWUvMTIpK3NpbigyKnBpKnRpbWUvMTIpKSkKZiA8LSBmdW5jdGlvbih4LGMpIGNvZWYobG1fcDMpWzFdICsgCiAgICAgICAgICAgICAgICAgICBjb2VmKGxtX3AzKVsyXSp4ICsKICAgICAgICAgICAgICAgICAgIGNvZWYobG1fcDMpWzNdKmNvcygyKnBpKngvMTIpCgpwbDEgICsgc3RhdF9mdW5jdGlvbihmdW49ZiwgY29sb3VyPSJsaWdodGdyZWVuIiwgc2l6ZT0xKSsgCiAgICAgICBnZ3RpdGxlKCJQb2x5bm9taWFsIE1vZGVsIHdpdGggcGVyaW9kaWMgY29tcG9uZXQgY29zKCkgKyBzaW4oKSIpCmBgYAo8YnIvPiBXZSBhcmUgZ29pbmcgdG8gcGVyZm9ybSBub3cgdGhlIEFJQyBhbmQgQklDIHRlc3QgaW4gb3JkZXIgdG8gY2hlY2sgd2hpY2ggbW9kZWwgZml0cyBiZXR0ZXIgb3VyIGRhdGEuIDxici8+CmBgYHtyfQpBSUMobG1fcDEsbG1fcDIsbG1fcDMpCmBgYAoKYGBge3J9CkJJQyhsbV9wMSxsbV9wMixsbV9wMykKYGBgCjxici8+IFdlIGNhbiBzZWUgaG93IGlzIHN1Z2dlc3RlZCBieSBBSUMgYW5kIEJJQyB0aGF0IGxtXzMgaXMgdGhlIG1vZGVsIHRoYXQgYmVzdCBkZXNjcmliZXMgdGhlIGJlaGF2aW91ciBvZiB0aGUgcXVhcnRlcmx5IHNhbGVzIHZvbHVtZS4KYGBge3J9CmNvZWYobG1fcDMpCmBgYAo8YnIvPgpUaGVyZWZvcmUsIHRoZSBtb2RlbCB0aGF0IGRlc2NyaWJlcyBvdXIgZGF0YSBpcyBkZWZpbmUgYXMgZm9sbG93aW5nOgokJHlfaj05OS42OSswLjM4eF9qKzEuNzUqXGJpZ2dbY29zXGJpZ2coXGZyYWN7MiBccGkgeF9qfXsxMn1cYmlnZykrc2luXGJpZ2coXGZyYWN7MiBccGkgeF9qfXsxMn1cYmlnZylcYmlnZ10rZV9qIFxxdWFkOyBccXVhZDHiiaRq4omkbiQkCjxici8+Cjxmb250IHNpemU9IjMiIGNvbG9yPSJNZWRpdW1TZWFHcmVlbiI+CipkLiBWYWxpZGF0aW5nIHRoZSByZXN1bHRzKgo8L2ZvbnQ+PGJyLz4KYGBge3J9CmYgPC0gZnVuY3Rpb24oeCxjKSBjb2VmKGxtX3AzKVsxXSArIAogICAgICAgICAgICAgICAgICAgY29lZihsbV9wMylbMl0qeCArIAogICAgICAgICAgICAgICAgICAgY29lZihsbV9wMylbM10qKGNvcygyKnBpKngvMTIpK3NpbigyKnBpKngvMTIpKQpwbDEgICsgc3RhdF9mdW5jdGlvbihmdW49ZiwgY29sb3VyPSJjb3JhbCIsIHNpemU9MSkgKyBnZ3RpdGxlKCJRdWFydGVybHkgVm9sdW1lIFNhbGVzIFByZWRpY3Rpb24gdnMgUmVhbCB2YWx1ZXMiKQpgYGAKYGBge3J9CnBhcihtZnJvdyA9IGMoMSwgMSkpCnBsb3QobG1fcDMsIHdoaWNoPWMoMSwxKSkKYGBgCjxici8+IFdlIGNhbiBzZWUgaG93IHRoZSByZXNpZHVhbHMgd2Ugc2hpZnQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVzaWR1YWxzIGZyb20gdGhlIG9iZSB3ZSBvYnRhaW5lZCBmb3IgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aG91dCB0aGUgcGVyaW9kaWMgY29tcG9uZW50LiBXZSBzdGlsbCBnZXQgYSBmYWlybHkgZGlzdHJpYnV0aW9uIG9mIHRoZSByZXNpZHVhbHMgYXJvdW5kIHplcm8uIFdlIGNhbiBhZ3JlZWUgdGhhdCB0aGlzIG1vZGVsIGZpdHMgYmV0dGVyIG91ciBkYXRhIHRoYXQgdGhlIG9uZSB3ZSBkZWZpbmVkIGJlZm9yZS48YnIvPgo8YnIvPgo8Zm9udCBzaXplPSIzIiBjb2xvcj0iTWVkaXVtU2VhR3JlZW4iPgoqZS4gQWRkaW5nIGEgY29uc3RyYWludCBvbiB0aGUgaW50ZXJjZXB0Kgo8L2ZvbnQ+PGJyLz4KV2Ugd2FudCB0aGUgcHJlZGljdGVkIHNhbGVzIHZvbHVtZSB0byBiZSBlcXVhbCB0byAxMDAgYXQgdGltZSAwLiBUaGVyZWZvcmUgd2UgcmVkZWZpbmVkIG91ciBtb2RlbCBhczoKYGBge3J9CmxtX3BlcmlvZGljMiA8LSBsbShkYXRhID0gc2FsZXMxLCBJKHktMTAwKSB+IDAgKyB0aW1lKyBJKGNvcygyKnBpKnRpbWUvMTIpK3NpbigyKnBpKnRpbWUvMTIpKSkKc3VtbWFyeShsbV9wZXJpb2RpYzIpCmYgPC0gZnVuY3Rpb24oeCxjKSAgMTAwICsgY29lZihsbV9wZXJpb2RpYzIpWzFdICogeCArIAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvZWYobG1fcGVyaW9kaWMyKVsyXSAqIChjb3MoMipwaSp4LzEyKStzaW4oMipwaSp4LzEyKSkKcGwxICArIHN0YXRfZnVuY3Rpb24oZnVuPWYsIGNvbG91cj0ibGlnaHRjb3JhbCIsIHNpemU9MSkKYGBgCmBgYHtyfQpwYXIobWZyb3cgPSBjKDEsIDEpKQpwbG90KGxtX3BlcmlvZGljMiwgd2hpY2g9YygxLDEpKQpgYGAKPGJyLz5BZGRpbmcgdGhlIGNvbnN0cmFpbnQgb3ZlciB0aGUgaW50ZXJjZXB0IGhhcyBhIHNsaWd0aCBpbXBhY3Qgb24gdGhlIGZpdHRpbmcgb2YgdGhlIG1vZGVsLiBXaGljaCBtYWtlcyBzZW5zZSBzaW5jZSB3ZSBhcmUgbm90IHRyeWluZyB0aGUgbW9kZWwgdG8gZml0IHRoZSBpbnRlcmNlcHQgdG8gdGhlIGRhdGEgYXZhaWxhYmxlLCBpbnN0ZWFkIHdlIGFyZSBmb3JjaW5nIGl0IHRvIGEgZGVmdWFsdCB2YWx1ZSBvZiAxMDAuPGJyLz4KPGJyLz4KPGJyLz4KPGZvbnQgc2l6ZT0iNSIgY29sb3I9InNlYWdyZWVuIj48Yj4KKjIuIEZpdHRpbmcgYSBsaW5lYXIgbWl4ZWQgZWZmZWN0cyBtb2RlbCoKPC9iPjwvZm9udD4KTm93IHdlIGh2ZSB0aGUgZGF0YSBvZiBxdWFydGVybHkgc2FsZXMgdm9sdW1lcyBvZiAzMCBkaWZmZXJlbnQgcHJvZHVjdHMuPGJyLz4KPGJyLz4KPGZvbnQgc2l6ZT0iMyIgY29sb3I9Ik1lZGl1bVNlYUdyZWVuIj4KKmEuIFBsb3R0aW5nIHRoZSBkYXRhKgo8L2ZvbnQ+PGJyLz4KYGBge3J9CnNhbGVzMzAgPC0gcmVhZC5jc3YoIi9Vc2Vycy9tYXJpYWZjYWRlbmEvRG9jdW1lbnRzL01hc3RlciBEZWdyZWUvUG9seXRlY2huaXF1ZS9TdGF0aXN0aWNzIGluIEFjdGlvbi9DYXNlIFN0dWR5IDIvc2FsZXNEYXRhL3NhbGVzMzAuY3N2IikKcGwyICAgICA8LWdncGxvdChzYWxlczMwKSArIGdlb21fcG9pbnQoYWVzKHg9dGltZSwgeT15KSxzaXplPTEsIGNvbG91cj0iUm95YWxibHVlIikgKyBnZ3RpdGxlKCJRdWFydGVybHkgc2FsZXMgdm9sdW1lcyIpCnByaW50KHBsMikKYGBgCgpgYGB7cn0KcGwzICAgPC0gZ2dwbG90KHNhbGVzMzApICsgCiAgICAgICAgIGdlb21fcG9pbnQoYWVzKHg9dGltZSwgeT15KSxzaXplPTEsIGNvbG91cj0iUm95YWxibHVlIikgKyAKICAgICAgICAgZ2d0aXRsZSgiUXVhcnRlcmx5IHNhbGVzIHZvbHVtZXMiKSArIAogICAgICAgICBmYWNldF93cmFwKH5pZCwgbmNvbD02KSAKcHJpbnQocGwzKQpgYGAKPGJyLz4KPGZvbnQgc2l6ZT0iMyIgY29sb3I9Ik1lZGl1bVNlYUdyZWVuIj4KKmIuIEZpdHRpbmcgYSBtb2RlbCB0byB0aGUgZmlyc3QgcHJvZHVjdCoKPC9mb250Pjxici8+Cjxici8+CldlIGFyZSBnb2luZyB0byBmaXQgdGhlIG1vZGVsIHVzZWQgcHJldmlvdXNseSBmb3IgZml0dGluZyB0aGUgZmlyc3Qgc2VyaWVzIHRvIHRoaXMgZGF0YS4KYGBge3J9CmxtX3BlcmlvZGljMyA8LSBsbShkYXRhID0gc3Vic2V0KHNhbGVzMzAsIGlkPT0xKSwgeSB+IHRpbWUgKyBJKGNvcygyKnBpKnRpbWUvMTIpK3NpbigyKnBpKnRpbWUvMTIpKSkKc3VtbWFyeShsbV9wZXJpb2RpYzMpCmBgYApgYGB7cn0KZiA8LSBmdW5jdGlvbih4LGMpIGNvZWYobG1fcGVyaW9kaWMzKVsxXSArIAogICAgICAgICAgICAgICAgICAgY29lZihsbV9wZXJpb2RpYzMpWzJdKnggKyAKICAgICAgICAgICAgICAgICAgIGNvZWYobG1fcGVyaW9kaWMzKVszXSooY29zKDIqcGkqeC8xMikrc2luKDIqcGkqeC8xMikpCnBsMyA8LWdncGxvdChzdWJzZXQoc2FsZXMzMCxpZD09MSkpICsgZ2VvbV9wb2ludChhZXMoeD10aW1lLCB5PXkpLHNpemU9MiwgY29sb3VyPSJSb3lhbGJsdWUiKSArIGdndGl0bGUoIlF1YXJ0ZXJseSBzYWxlcyB2b2x1bWVzIGZpcnN0IHNlcmllcyIpCnBsMyAgKyBzdGF0X2Z1bmN0aW9uKGZ1bj1mLCBjb2xvdXI9ImxpZ2h0Y29yYWwiLCBzaXplPTEpCmBgYApgYGB7cn0KcGFyKG1mcm93ID0gYygxLCAxKSkKcGxvdChsbV9wZXJpb2RpYzMsIHdoaWNoPWMoMSwxKSkKYGBgCjxici8+V2UgY2FuIHNlZSBob3cgd2UgZ2V0IG1vcmUgb3IgbGVzcyB0aGUgc2FtZSByZXN1bHRzIGFzIHRoZSBvbmVzIHdlIG9idGFpbmVkIHdpdGggdGhlIHByZXZpb3VzIGRhdGEuIFNvIHdlIGNhbiBhZ3JlZSB0aGF0IHRoaXMgbW9kZWxzIHN1aXRzIHVzIGZvciBwcmVkaWN0aW5nIHRoZSBzYWxlcyB2b2x1bWUgb2Ygb25lIHByb2R1Y3Qgb24gdGhlIGRhdGEgc2V0Ljxicj4KPGJyPgo8Zm9udCBzaXplPSIzIiBjb2xvcj0iTWVkaXVtU2VhR3JlZW4iPgoqYy4gRml0dGluZyBhIG1peGVkIGVmZmVjdCBtb2RlbCoKPC9mb250Pjxici8+CldlIGhhdmUgYmVlbiBhYmxlIHRvIGNyZWF0ZSBhIG1vZGVsIHRoYXQgZGVzY3JpYmVzIHByZXR0eSBhY2N1cmF0ZWx5IHRoZSBwcmVkaWN0aW9uIG9mIHRoZSBzYWxlcyB2b2x1bWVzIG9mIG9uZSBwcm9kdWN0LiBOb3cgaWYgd2Ugd2FudCB0byBjb25zdHJ1Y3QgYSBtb2RlbCBmb3IgZGlzY3JpYmluZyB0aGUgb3ZlcmFsbCBiZWhhdmlvdXIgb2YgdGhlIHNhbGVzIHZvbHVtZSBmb3IgdGhlIGRpZmZlcmVudCBwcm9kdWN0cyBpdHMgaW1wb3J0YW50IHRvIHRha2UgaW50byBhY2NvdW50IHRoZSBlZmZlY3QgdGhhdCBlYWNoIHByb2R1Y3QgbWF5IGhhdmUgb3ZlciB0aGUgcHJlZGljaW9udC4gRnJvbSB0aGUgc3RhcnQgaXMgbm90IGNsZWFyIHRvIGtub3cgb3ZlciB3aGljaCBjb21wb25lbnQgb2YgdGhlIG1vZGVsIHRoZSB0eXBlIG9mIHByb2R1Y3Qgd2lsbCBoYXZlIGFuZCBpbXBhY3QuIFRoZXJlZm9yZSB3ZSBhcmUgZ29pbmcgdG8gZGVmaW5lIHNldmVyYWwgZGlmZmVyZW50IG1vZGVscyBhc3N1bWluZyBhbGwgdGhlIHNjZW5hcmlvcyB3aGVyZSBmYWN0b3JzIGNvdWxkIGRlcGVuZCBvbiB0aGUgcHJvZHVjdC4gVGhlbiB3ZSBhcmUgZ29pbmcgdG8gdGVzdCBhbmQgY2hvb3NlIHRoZSBtb2RlbCB0aGF0IGJlc3QgZml0cyBvdXIgZGF0YS48YnIvPgo8YnIvPgo8Yj5Nb2RlbCAxPC9iPjogTm90aGluZyBkZXBlbmRzIG9uIHRoZSBwcm9kdWN0OgokJHlfe2lqfT1cYmV0YV97MH0rXGJldGFfezF9Knhfe2lqfStcYmV0YV97Mn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK2Vfe2lqfSQkCjxici8+CjxiPk1vZGVsIDI8L2I+OiBUaGUgdmFsdWUgb2YgdGhlIGludGVyY2VwdCAoc2FsZXMgdm9sdW1lIGF0IHRpbWUgMCkgZGVwZW5kcyBvbiB0aGUgcHJvZHVjdDoKJCR5X3tpan09XGJldGFfezB9K1xiZXRhX3sxfSp4X3tpan0rXGJldGFfezJ9KlxiaWdnW2Nvc1xiaWdnKFxmcmFjezJccGkgeF97aWp9fXsxMn1cYmlnZykrc2luXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKVxiaWdnXStcZXRhX3tpMH0rZV97aWp9JCQKPGJyLz4KPGI+TW9kZWwgMzwvYj46IFRoZSBncm93dGggcmF0ZSBwZXIgcXVhcnRlciBkZXBlbmRzIG9uIHRoZSBwcm9kdWN0OgokJHlfe2lqfT1cYmV0YV97MH0rXGJldGFfezF9Knhfe2lqfStcYmV0YV97Mn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK1xldGFfe2kxfSp4X3tpan0rZV97aWp9JCQKPGJyLz4KPGI+TW9kZWwgNDwvYj46IFRoZSBpbnRlcmNlcHQgYW5kIHRoZSBncm93dGggcmF0ZSBwZXIgcXVhcnRlciBkZXBlbmRzIG9uIHRoZSBwcm9kdWN0OgokJHlfe2lqfT1cYmV0YV97MH0rXGJldGFfezF9Knhfe2lqfStcYmV0YV97Mn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK1xldGFfe2kwfStcZXRhX3tpMX0qeF97aWp9K2Vfe2lqfSQkCjxici8+CjxiPk1vZGVsIDU8L2I+OiBUaGUgcGVyaW9kaWMgY29tcG9uZW50IGRlcGVuZHMgb24gdGhlIHByb2R1Y3Q6CiQkeV97aWp9PVxiZXRhX3swfStcYmV0YV97MX0qeF97aWp9K1xiZXRhX3syfSpcYmlnZ1tjb3NcYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpK3NpblxiaWdnKFxmcmFjezJccGkgeF97aWp9fXsxMn1cYmlnZylcYmlnZ10rXGV0YV97aTJ9KlxiaWdnW2Nvc1xiaWdnKFxmcmFjezJccGkgeF97aWp9fXsxMn1cYmlnZykrc2luXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKVxiaWdnXStlX3tpan0kJAo8YnIvPgo8Yj5Nb2RlbCA2PC9iPjogVGhlIGludGVyY2VwdCBhbmQgdGhlIHBlcmlvZGljIGNvbXBvbmVudCBkZXBlbmRzIG9uIHRoZSBwcm9kdWN0OgokJHlfe2lqfT1cYmV0YV97MH0rXGJldGFfezF9Knhfe2lqfStcYmV0YV97Mn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK1xldGFfe2kwfStcZXRhX3tpMn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK2Vfe2lqfSQkCjxici8+CjxiPk1vZGVsIDc8L2I+OiBUaGUgZ3Jvd3RoIHJhdGUgcGVyIHF1YXJ0ZXIgYW5kIHBlcmlvZGljIGNvbXBvbmVudCBkZXBlbmRzIG9uIHRoZSBwcm9kdWN0OgokJHlfe2lqfT1cYmV0YV97MH0rXGJldGFfezF9Knhfe2lqfStcYmV0YV97Mn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK1xldGFfe2kxfSp4X3tpan0rXGV0YV97aTJ9KlxiaWdnW2Nvc1xiaWdnKFxmcmFjezJccGkgeF97aWp9fXsxMn1cYmlnZykrc2luXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKVxiaWdnXStlX3tpan0kJAo8YnIvPgo8Yj5Nb2RlbCA4PC9iPjogRXZlcmV5dGhpbmcgZGVwZW5kcyBvbiB0aGUgcHJvZHVjdDoKJCR5X3tpan09XGJldGFfezB9K1xiZXRhX3sxfSp4X3tpan0rXGJldGFfezJ9KlxiaWdnW2Nvc1xiaWdnKFxmcmFjezJccGkgeF97aWp9fXsxMn1cYmlnZykrc2luXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKVxiaWdnXStcZXRhX3tpMH0rXGV0YV97aTF9Knhfe2lqfStcZXRhX3tpMn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK2Vfe2lqfSQkCgpgYGB7cn0KbTEgPC0gbG0oeSB+ICAgdGltZSArIEkoY29zKDIqcGkqdGltZS8xMikgKyBzaW4oMipwaSp0aW1lLzEyKSksIGRhdGE9c2FsZXMzMCkgIAptMiA8LSBsbWVyKHkgfiB0aW1lICsgSShjb3MoMipwaSp0aW1lLzEyKSArIHNpbigyKnBpKnRpbWUvMTIpKSArICgxfGlkKSwgZGF0YT1zYWxlczMwKSAKbTMgPC0gbG1lcih5IH4gdGltZSArIEkoY29zKDIqcGkqdGltZS8xMikgKyBzaW4oMipwaSp0aW1lLzEyKSkgKyAoLTEgKyB0aW1lfGlkKSAsIGRhdGE9c2FsZXMzMCkgCm00IDwtIGxtZXIoeSB+IDEgKyB0aW1lKyBJKGNvcygyKnBpKnRpbWUvMTIpICsgc2luKDIqcGkqdGltZS8xMikpICsgKHRpbWV8aWQpICwgZGF0YT1zYWxlczMwKSAKbTUgPC0gbG1lcih5IH4gIHRpbWUrIEkoY29zKDIqcGkqdGltZS8xMikgKyBzaW4oMipwaSp0aW1lLzEyKSkgKyAoLTErSShjb3MoMipwaSp0aW1lLzEyKSArIHNpbigyKnBpKnRpbWUvMTIpKXxpZCkgLCBkYXRhPXNhbGVzMzApIAptNiA8LSBsbWVyKHkgfiAxICt0aW1lKyBJKGNvcygyKnBpKnRpbWUvMTIpICsgc2luKDIqcGkqdGltZS8xMikpICsgKEkoY29zKDIqcGkqdGltZS8xMikgKyBzaW4oMipwaSp0aW1lLzEyKSl8aWQpICwgZGF0YT1zYWxlczMwKSAKbTcgPC0gbG1lcih5IH4gdGltZSsgSShjb3MoMipwaSp0aW1lLzEyKSArIHNpbigyKnBpKnRpbWUvMTIpKSArICgtMSt0aW1lK0koY29zKDIqcGkqdGltZS8xMikgKyBzaW4oMipwaSp0aW1lLzEyKSl8aWQpICwgZGF0YT1zYWxlczMwKSAKbTggPC0gbG1lcih5IH4gMSsgdGltZSsgSShjb3MoMipwaSp0aW1lLzEyKSArIHNpbigyKnBpKnRpbWUvMTIpKSArICh0aW1lK0koY29zKDIqcGkqdGltZS8xMikgKyBzaW4oMipwaSp0aW1lLzEyKSl8aWQpICwgZGF0YT1zYWxlczMwKQoKQklDKG0xLG0yLG0zLG00LCBtNSxtNixtNywgbTgpCmBgYAo8L2JyPlRoZSBCSUMgc3VnZ2VzdCB0aGVuIHRoYXQgdGhlIG1vZGVsIHRoYXQgYmVzdCBmaXQgb3VyIGRhdGEgYW5kIGV4cGxhaW5zIHRoZSBlZmZlY3QgZWFjaCBwcm9kdWN0IGhhcyBvdmVyIHRoZSBnZW5lcmFsIG1vZGVsIGlzIG1vZGVsIDcuIFRoaXMgbW9kZWxzIHN1Z2dlc3QgdGhhdCBlYWNoIHByb2R1Y3RzIGludHJvZHVjZSBhIHJhbmRvbSBlZmZlY3QgaW4gdGhlIGdyb3d0aCByYXRlIGFuZCBhbHNvIG9uIHRoZSBwZXJpb2RpYyBjb21wb25lbnQgYnV0IHRoZSBpbnRlcmNlcHQgcmVtYWlucyB0aGUgc2FtZSBmb3IgYWxsIHRoZSBwcm9kdWN0cy48L2JyPgpgYGB7cn0KbTcKYGBgCjwvYnI+VGhlcmVmb3JlIHRoZSBtaXhlZCBlZmZlY3QgbW9kZWwgdGhhdCBkZXNjcmliZXMgb3VyIGRhdGEgaXMgZGVmaW5lZCBhcyBmb2xsb3dpbmc6PC9icj4KJCR5X3tpan09MTAwLjEyKzAuMjEqeF97aWp9KzEuMDYqXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK1xldGFfe2kxfSp4X3tpan0rXGV0YV97aTJ9KlxiaWdnW2Nvc1xiaWdnKFxmcmFjezJccGkgeF97aWp9fXsxMn1cYmlnZykrc2luXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKVxiaWdnXStlX3tpan0kJAo8YnIvPgo8Zm9udCBzaXplPSIzIiBjb2xvcj0iTWVkaXVtU2VhR3JlZW4iPgoqZC4gUGxvdHRpbmcgdGhlIHByZWRpY3RlZCB2YWx1ZXMqCjwvZm9udD48YnIvPgo8YnIvPgpXZSBhcmUgZ29pbmcgdG8gcGxvdCB0aGUgZGF0YSB3aXRoIHRoZSBwcmVkaWN0ZWQgc2FsZXMgZ2l2ZW4gYnkgb3VyIGZpbmFsIG1vZGVsLgpgYGB7cn0KcHJlZGljdGlvbiA8LSBmaXR0ZWQobTcpCmdncGxvdChkYXRhPXNhbGVzMzApICsgZ2VvbV9wb2ludChhZXMoeD10aW1lLHk9eSksIGNvbG9yPSJsaWdodHNlYWdyZWVuIiwgc2l6ZT0xKSArIAogIGdlb21fbGluZShhZXMoeD10aW1lLHk9cHJlZGljdGlvbiksIGNvbG9yPSJSb3lhbGJsdWUiKSArIGZhY2V0X3dyYXAofmlkLCBuY29sPTYpIApgYGAKYGBge3J9CnFxbm9ybShyZXNpZHVhbHMobTcpKQpgYGAKPC9icj5BcyB3ZSBjYW4gc2VlIHRoZSBwbG90cyBhYm92ZSwgb3VyIG1vZGVsIGZpdHMgcHJldHR5IHdlbGwgZXZlcnkgb25lIG9mIHRoZSAzMCBwcm9kdWN0cyBzaW5jZSB0aGUgcXVhbnRpbGUgcGxvdCBkb2VzIG5vdCByYWlzZSBhbnkgc2lnbmlmaWNhbnQgY29uY2VybiB3aXRoIG5vcm1hbGl0eSBvZiB0aGUgcmVzaWR1YWxzLjxici8+Cjxici8+Cjxmb250IHNpemU9IjMiIGNvbG9yPSJNZWRpdW1TZWFHcmVlbiI+CipkLiBBZGRpbmcgYSBjb25zdHJhaW50IG92ZXIgdGhlIGludGVyY2VwdCoKPC9mb250Pjxici8+Cjxici8+CldlIGFyZSBnb2luZyB0byBzZXQgdGhlIHByZWRpY3RlZCBzYWxlcyB2b2x1bWUgb2YgYWxsIHByb2R1Y3RzIGVxdWFsIHRvIDEwMCBhdCB0aW1lIDAuPGJyLz4KYGBge3J9CmNvbnN0cmFpbnRfbW9kZWwgPC0gbG1lcihJKHktMTAwKSB+IDAgK3RpbWUrIEkoY29zKDIqcGkqdGltZS8xMikgKyBzaW4oMipwaSp0aW1lLzEyKSkgKyAoLTErdGltZStJKGNvcygyKnBpKnRpbWUvMTIpICsgc2luKDIqcGkqdGltZS8xMikpfGlkKSwgZGF0YSA9IHNhbGVzMzApCmY8LWZpdHRlZChjb25zdHJhaW50X21vZGVsKQpnZ3Bsb3QoZGF0YT1zYWxlczMwKSArIGdlb21fcG9pbnQoYWVzKHg9dGltZSx5PXkpLCBjb2xvcj0ibGlnaHRzZWFncmVlbiIsIHNpemU9MSkgKyAKICBnZW9tX2xpbmUoYWVzKHg9dGltZSx5PWYrMTAwKSwgY29sb3I9IlJveWFsYmx1ZSIpICsgZmFjZXRfd3JhcCh+aWQsIG5jb2w9NikgCmBgYApgYGB7cn0KcXFub3JtKHJlc2lkdWFscyhjb25zdHJhaW50X21vZGVsKSkKYGBgCjwvYnI+T25jZSBhZ2FpbiB3ZSBzZWUgaG93IGFkZGluZyB0aGUgY29uc3RyYWludCwgc3RpbGwgZ2V0cyB1cyBhIHdlbGwgZml0dGVkIG1vZGVsIGFuZCBhcyB3ZSBjYW4gc2VlIGluIHRoZSBxdWFudGlsZSBwbG90IGl0IGRvZXMgbm90ICBoYXZlIGEgbmVnYXRpdmUgaW1wYWN0IG9uIHRoZSBlZmZlY3RpdmVuZXNzIG9mIG91ciBtb2RlbC48L2JyPgo8L2JyPgo8L2JyPgo8Zm9udCBzaXplPSI1IiBjb2xvcj0ic2VhZ3JlZW4iPjxiPgoqMi4gSW5kaXZpZHVhbCBwcmVkaWN0aW9uKgo8L2I+PC9mb250PjwvYnI+CjwvYnI+Ck5vdyB3ZSBhcmUgZ29pbmcgdG8gc2VlIGhvdyB3ZWxsIGRvZXMgb3VyIG1vZGVsIHBlcmZvcm0gZm9yIHByZWRpY3RpbmcgdGhlIHNhbGVzIHZvbHVtZSBmb3IgYSBuZXcgcHJvZHVjdC48L2JyPgpgYGB7cn0KZGF0YV9wYXRoIDwtICIvVXNlcnMvbWFyaWFmY2FkZW5hL0RvY3VtZW50cy9NYXN0ZXIgRGVncmVlL1BvbHl0ZWNobmlxdWUvU3RhdGlzdGljcyBpbiBBY3Rpb24vQ2FzZSBTdHVkeSAyL3NhbGVzRGF0YS9zYWxlc05ldy5jc3YiCnNhbGVzTmV3ICAgIDwtIHJlYWQuY3N2KGZpbGU9ZGF0YV9wYXRoKQpgYGAKPGJyLz4KPGZvbnQgc2l6ZT0iMyIgY29sb3I9Ik1lZGl1bVNlYUdyZWVuIj4KKmEuIFByZWRpY3Rpb24gd2l0aG91dCBhbnkgZGF0YSoKPC9mb250Pjxici8+Cjxici8+CldpdGhvdXQgYW55IGluZm9ybWF0aW9uIG9uIGhvdyBhcmUgdGhlIHNhbGVzIGZvciB0aGlzIG5ldyBwcm9kdWN0IHdlIHdpbGwgYXNzdW1lIHRoYXQgd2UgY2FuIHByZWRpY3QgdGhlIHNhbGVzIHdpdGggdGhlIGZpeGVkIHBhcnRzIG9mIHRoZSBtb2RlbCB3ZSBwcmV2aW91c2x5IGRlZmluZWQuCjxici8+CkdpdmVuIHRoYXQgb3VyIG1vZGVsIGlzOgokJHlfe2lqfT1cYmV0YV97MH0rXGJldGFfezF9Knhfe2lqfStcYmV0YV97Mn0qXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddK1xldGFfe2kxfSp4X3tpan0rXGV0YV97aTJ9KlxiaWdnW2Nvc1xiaWdnKFxmcmFjezJccGkgeF97aWp9fXsxMn1cYmlnZykrc2luXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKVxiaWdnXStlX3tpan0kJApXZSB3aWxsIGFzc3VtZSB0aGF0ICRcZXRhX3tpMH0kLCAkXGV0YV97aTF9JCwgYW5kICRcZXRhX3tpMn0kIGFyZSB6ZXJvIHNpbmNlIHdlIGRvbnQgaGF2ZSBhbnlkYXRhIHRvIGRlZmluZSB0aGUgcmFuZG9tIGVmZmVjdCB0aGlzIHByb2R1Y3Qgd2lsbCBoYXZlIG92ZXIgdGhlIHBvcHVsYXRpb24gdmFsdWVzLjxici8+Cjxici8+ClRoZXJlZm9yZSBvdXIgZXF1YXRpb24gZm9yIHByZWRpY3Rpb24gdGhlIHZhbHVlcyBvZiB0aGlzIHByb2R1Y3Qgd2lsbCBiZToKJCR5X3tpan09MTAwLjEyKzAuMjEqeF97aWp9KzEuMDYqXGJpZ2dbY29zXGJpZ2coXGZyYWN7MlxwaSB4X3tpan19ezEyfVxiaWdnKStzaW5cYmlnZyhcZnJhY3syXHBpIHhfe2lqfX17MTJ9XGJpZ2cpXGJpZ2ddJCQKKEludGVyY2VwdCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCkkoY29zKDIgKiBwaSAqIHRpbWUvMTIpICsgc2luKDIgKiBwaSAqIHRpbWUvMTIpKSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKYGBge3J9CmYgPC0gZnVuY3Rpb24oeCxjKSAxMDAuMTIyNSArIAogICAgICAgICAgICAgICAgICAgMC4yMTU1KnggKyAKICAgICAgICAgICAgICAgICAgIDEuMDY2NiooY29zKDIqcGkqeC8xMikrc2luKDIqcGkqeC8xMikpCnBsNCAgICAgICA8LSBnZ3Bsb3Qoc2FsZXNOZXcpICsgCiAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcyh4PXRpbWUsIHk9eSksc2l6ZT0zLCBjb2xvdXI9IlJveWFsYmx1ZSIpICsKICAgICAgICAgICAgIHlsYWIoIiUgU2FsZXMgVm9sdW1lcyIpICsKICAgICAgICAgICAgIHhsYWIoInRpbWUiKQpwcmludChwbDQgKyBnZ3RpdGxlKCJRdWFydGVybHkgc2FsZXMgdm9sdW1lcyBuZXcgcHJvZHVjdCIpICsgc3RhdF9mdW5jdGlvbihmdW49ZiwgY29sb3VyPSJsaWdodGNvcmFsIiwgc2l6ZT0xKSkKYGBgCjxici8+QXMgd2UgY2FuIHNlZSwgdGhlIGZpeGVkIHBhcnQgb2Ygb3VyIG1vZGVsIGdpdmVzIHVzIGFuIGFwcHJveGltYXRpb24gb2YgdGhlIHNhbGVzIHZvbHVtZSBmb3IgdGhlIHByb2R1Y3QuIEJ1dCBpdHMgY2xlYXIgdGhhdCB0aGVyZSBpcyBhIG5lZWQgb24gaGF2aW5nIHNvbWUgZGF0YSBmcm9tIHRoZSBwcmV2aW91cyBzYWxlcyBiZWhhdmlvdXIgb2YgcHJvZHVjdCwgdG8gZml0IG9yIHRyYWluIG91ciBtb2RlbCB0byBwcmVkaWN0IG1vcmUgYWNjdXJhdGVseSB0aGUgc2FsZXMgZm9yIHRoaXMgbmV3IHByb2R1Y3QuIE9yIGluIG90aGVyIHdvcmRzIHRvIHVuZGVyc3RhbmQgdGhlIHJhbmRvbSBlZmZlY3QgdGhhdCB0aGlzIHByb2R1Y3QgaGFzIG9uIHRoZSAicG9wdWxhdGlvbiIgZ3Jvd3RoIGFuZCBwZXJpb2RpY2FsIGNvbXBvbmVudC48YnIvPgo8YnIvPgo8Zm9udCBzaXplPSIzIiBjb2xvcj0iTWVkaXVtU2VhR3JlZW4iPgoqYi4gUHJlZGljdGlvbiB3aXRoIG9uZSBkYXRhIHBvaW50IGZvciB0aGUgbmV3IHByb2R1Y3QqCjwvZm9udD48YnIvPgo8YnIvPgpUbyBhZGp1c3QgdGhlIG1vZGVsIHdlIGFyZSBnb2luZyB0byBmaXQgYWdhaW4gdGhlIG1peGVkIGVmZmVjdCBtb2RlbCB3aXRoIHRoZSBkYXRhIGZyb20gdGhlIG5ldyBwcm9kdWN0LiBTaW5jZSB3ZSBvbmx5IGhhdmUgZGF0YSBmb3Igb25lIHBlcmlvZCAodGltZT0xKSwgdGhlbiB3ZSBhcmUgZ29pbmcgdG8gYXNzdW1lIHRoZSBwb3B1bGF0aW9uIHZhbHVlcyBmb3IgdGhlIG90aGVyIHF1YXJ0ZXJzIGFuZCB0cmFpbiB0aGUgbW9kZWwgd2l0aCB0aGlzIGRhdGEuPGJyLz4KYGBge3J9Ck5ld19EYXRhPC1zYWxlczMwCk5ld19Qcm9kdWN0PC1hcy5kYXRhLmZyYW1lKGNiaW5kKG1hdHJpeCgzMSwgbmNvbCA9IDEsIG5yb3cgPSBucm93KHNhbGVzTmV3KSksIHNhbGVzTmV3JHRpbWUpKQpmIDwtIGZ1bmN0aW9uKHgsYykgMTAwLjEyMjUgKyAKICAgICAgICAgICAgICAgICAgIDAuMjE1NSp4ICsgCiAgICAgICAgICAgICAgICAgICAxLjA2NjYqKGNvcygyKnBpKngvMTIpK3NpbigyKnBpKngvMTIpKQpOZXdfUHJvZHVjdDwtY2JpbmQoTmV3X1Byb2R1Y3QsIGxhcHBseShOZXdfUHJvZHVjdFsyXSwgZikpCmNvbG5hbWVzKE5ld19Qcm9kdWN0KSA8LSBjKCJpZCIsICJ0aW1lIiwieSIpCiNXZSBjcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggdGhlIG5ldyBwcm9kdWN0LCB0aGUgdmFsdWVzIHRoYXQgd2UgdXNlZCBmb3IgdGhlIHNhbGVzIHZvbHVtZSAoeSkgYXJlIHRoZSBvbmVzIHByZWRpY3RlZCBieSB0aGUgZml4ZWQgCk5ld19EYXRhPC1yYmluZChOZXdfRGF0YSwgTmV3X1Byb2R1Y3QpCiNXZSB1cGRhdGUgdGhlIHZhbHVlIG9mIHRpbWUgMSBmb3IgdGhlIG5ldyBwcm9kdWN0IHdpdGggdGhlIGtub3duIHZhbHVlCnRpbWUxPC1zYWxlc05ldyR5W3NhbGVzTmV3JHRpbWUgPT0gMSBdIApOZXdfRGF0YSR5W05ld19EYXRhJHRpbWU9PTEgJiBOZXdfRGF0YSRpZD09MzFdPC10aW1lMQpgYGAKCmBgYHtyfQojTm93IHdlIGZpdCBhZ2FpbiB0aGUgbWl4ZWQgZWZmZWN0IG1vZGVsIHdpdGggdGhpcyBuZXcgZGF0YQpOZXdfbW9kZWxfZDE8LSBsbWVyKHkgfiB0aW1lKyBJKGNvcygyKnBpKnRpbWUvMTIpICsgc2luKDIqcGkqdGltZS8xMikpICsgKC0xK3RpbWUrSShjb3MoMipwaSp0aW1lLzEyKSArIHNpbigyKnBpKnRpbWUvMTIpKXxpZCkgLCBkYXRhPU5ld19EYXRhKQpgYGAKPGJyLz5UbyB2YWxpZGF0ZSBob3cgdGhlIG5ldyBtb2RlbCBmaXRzIHRoZSBuZXcgcHJvZHVjdCwgd2UgYXJlIGdvaW5nIHRvIG9idGFpbiB0aGUgYmV0YXMgb3IgY29lZmZpY2llbnRzIGZyb20gdGhlIG1peGVkIGVmZmVjdCBtb2RlbCBmb3IgdGhlIG5ldyBwcm9kdWN0IChpZD0zMSkgYW5kIHRoZW4gY29udHJ1Y3QgdGhlIGZ1bmN0aW9uIHRvIHByZWRpY3QgdGhlIHNhbGVzIHZvbHVtZS4gV2l0aCB0aGlzIGluZm9ybWF0aW9uIHdlIGFyZSBnb2luZyB0byBwbG90IG91ciBwcmVkaWN0aW9uIHZzIHRoZSBhY3R1YWwgdmFsdWVzIHRvIHNlZSBob3cgd2VsbCB0aGUgbW9kZWwgcGVyZm9ybXMuPGJyLz4KYGBge3J9CmJldGFzPC1jYmluZChjb2VmKE5ld19tb2RlbF9kMSkkaWQsIG1hdHJpeChjKDE6MzEpLG5jb2w9MSwgbnJvdz0zMSkpCmNvbG5hbWVzKGJldGFzKSA8LSBjKCJiXzAiLCAiYl8xIiwiYl8yIiwgImlkIikKCmZfZDEgPC0gZnVuY3Rpb24oeCxjKSBiZXRhcyRiXzBbYmV0YXMkaWQ9PTMxXSArIAogICAgICAgICAgICAgICAgICAgYmV0YXMkYl8xW2JldGFzJGlkPT0zMV0qeCArIAogICAgICAgICAgICAgICAgICAgYmV0YXMkYl8yW2JldGFzJGlkPT0zMV0qKGNvcygyKnBpKngvMTIpK3NpbigyKnBpKngvMTIpKQoKcHJpbnQocGw0ICsgZ2d0aXRsZSgiUXVhcnRlcmx5IHNhbGVzIHZvbHVtZXMgbmV3IHByb2R1Y3QiKSArIHN0YXRfZnVuY3Rpb24oZnVuPWZfZDEsIGNvbG91cj0ibGlnaHRjb3JhbCIsIHNpemU9MSkpCmBgYAo8YnIvPkFuZCBjb21wdXRlIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyB0byB1bmRlcnN0YW5kIGhvdyBnb29kIHRoZSBtb2RlbCBwZXJmb3Jtcy48YnIvPgpgYGB7cn0KRmluYWxfRGF0YSAgICAgIDwtIGFzLmRhdGEuZnJhbWUoKHNhbGVzTmV3WzJdLWxhcHBseShzYWxlc05ld1sxXSwgZikpXjIpClByZWRpY3Rpb25FcnJvciA8LSBzdW0oRmluYWxfRGF0YSR5KQpQcmVkaWN0aW9uRXJyb3IKYGBgCjxici8+V2UgY2FuIHNlZSB0aGF0IHN0aWxsIHRoZSBtb2RlbCBkb2VzIG5vdCBnYXRoZXIgZW5vdWdoIGluZm9ybWF0aW9uIHRvIGNvcnJlY3RseSBkZWZpbmVkIHRoZSByYW5kb20gZWZmZWN0IHRoYXQgdGhlIG5ldyBwcm9kdWN0IGhhcyBvdmVyIHRoZSBmaXhlZCBtb2RlbCA8YnIvPgo8YnIvPgo8Zm9udCBzaXplPSIzIiBjb2xvcj0iTWVkaXVtU2VhR3JlZW4iPgoqYy4gUHJlZGljdGlvbiB3aXRoIGluY3JlYXNpbmcgZGF0YSBmb3IgdGhlIG5ldyBwcm9kdWN0Kgo8L2ZvbnQ+PGJyLz4KPGJyLz4KTm93IHdlIGFyZSBnb2luZyB0byBwZXJmb3JtIHRoZSBzYW1lIGNvbXB1dGF0aW9uIHRoYXQgd2UgZGlkIGJlZm9yZSwgYnV0IG5vdyBhZGRpbmcgYXQgYSB0aW1lIG9uZSBrbm93biB2YWx1ZSBvZiB0aGUgc2FsZXMgdm9sdW1lLiBUbyBzZWUgaG93IGFkZGluZyBpbmZvcm1hdGlvbiB0byB0aGUgbW9kZWwgaGVscHMgYXMgaW1wcm92ZSB0aGUgZml0dGluZyBvZiB0aGUgbW9kZWwuPGJyLz4KYGBge3J9Ck5ld19EYXRhPC1zYWxlczMwCk5ld19Qcm9kdWN0PC1hcy5kYXRhLmZyYW1lKGNiaW5kKG1hdHJpeCgzMSwgbmNvbCA9IDEsIG5yb3cgPSBucm93KHNhbGVzTmV3KSksIHNhbGVzTmV3JHRpbWUpKQpmIDwtIGZ1bmN0aW9uKHgsYykgMTAwLjEyMjUgKyAKICAgICAgICAgICAgICAgICAgIDAuMjE1NSp4ICsgCiAgICAgICAgICAgICAgICAgICAxLjA2NjYqKGNvcygyKnBpKngvMTIpK3NpbigyKnBpKngvMTIpKQpOZXdfUHJvZHVjdDwtY2JpbmQoTmV3X1Byb2R1Y3QsIGxhcHBseShOZXdfUHJvZHVjdFsyXSwgZikpCmNvbG5hbWVzKE5ld19Qcm9kdWN0KSA8LSBjKCJpZCIsICJ0aW1lIiwieSIpCiNXZSBjcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggdGhlIG5ldyBwcm9kdWN0LCB0aGUgdmFsdWVzIHRoYXQgd2UgdXNlZCBmb3IgdGhlIHNhbGVzIHZvbHVtZSAoeSkgYXJlIHRoZSBvbmVzIHByZWRpY3RlZCBieSB0aGUgZml4ZWQgCk5ld19EYXRhPC1yYmluZChOZXdfRGF0YSwgTmV3X1Byb2R1Y3QpCml0ZXI9MApmb3IgKGkgaW4gc2FsZXNOZXckdGltZSkKewogIGl0ZXI9aXRlcisxCiAgI1dlIHVwZGF0ZSB0aGUgdmFsdWUgb2YgeSB3aXRoIHRoZW4ga25vd24gc2FsZXMgdmFsdW1lIGZvciB0aGUgaSBxdWFydGVyCiAgc2FsZXNfaXF1YXJ0ZXI8LXNhbGVzTmV3JHlbc2FsZXNOZXckdGltZSA9PSBpIF0gCiAgTmV3X0RhdGEkeVtOZXdfRGF0YSR0aW1lPT1pICYgTmV3X0RhdGEkaWQ9PTMxXTwtc2FsZXNfaXF1YXJ0ZXIKICAKICAjV2UgZml0IHRoZSBtb2RlbCB0byB0aGUgbmV3IGRhdGEKICBOZXdfbW9kZWxfZHQ8LSBsbWVyKHkgfiB0aW1lKyBJKGNvcygyKnBpKnRpbWUvMTIpICsgc2luKDIqcGkqdGltZS8xMikpICsgKC0xK3RpbWUrSShjb3MoMipwaSp0aW1lLzEyKSArIHNpbigyKnBpKnRpbWUvMTIpKXxpZCkgLCBkYXRhPU5ld19EYXRhKQoKICAjd2Ugb2J0YWluIHRoZSBjb2VmZmljaWVudHMgYW5kIHJlLWNvbnN0cnVjdCB0aGUgbW9kZWwgZm9yIHRoZSBuZXcgcHJvZHVjdCAoaWQ9MzEpCiAgYmV0YXM8LWNiaW5kKGNvZWYoTmV3X21vZGVsX2R0KSRpZCwgbWF0cml4KGMoMTozMSksbmNvbD0xLCBucm93PTMxKSkKICBjb2xuYW1lcyhiZXRhcykgPC0gYygiYl8wIiwgImJfMSIsImJfMiIsICJpZCIpCgogIGYgPC0gZnVuY3Rpb24oeCxjKSBiZXRhcyRiXzBbYmV0YXMkaWQ9PTMxXSArIAogICAgICAgICAgICAgICAgICAgICBiZXRhcyRiXzFbYmV0YXMkaWQ9PTMxXSp4ICsgCiAgICAgICAgICAgICAgICAgICAgIGJldGFzJGJfMltiZXRhcyRpZD09MzFdKihjb3MoMipwaSp4LzEyKStzaW4oMipwaSp4LzEyKSkKICAKICAjV2UgY29tcHV0ZSB0aGUgbmV3IHByZWRpY3Rpb24gZm9yIHRoZSBzYWxlcyB2b2x1bWUgYW5kIHN0b3JlIGluIGEgZGF0YWZyYW1lIHRoZSByZXN1bHRzIG9mIG91ciBpdGVyYXRpb24KICBpZiAoaT09MSl7CiAgICBSZXN1bHRzPC1hcy5kYXRhLmZyYW1lKGNiaW5kKG1hdHJpeCh0b1N0cmluZyhpdGVyKSwgbmNvbCA9IDEsIG5yb3cgPSBucm93KHNhbGVzTmV3KSksc2FsZXNOZXckdGltZSxzYWxlc05ldyR5LCBhcy5kYXRhLmZyYW1lKGxhcHBseShzYWxlc05ld1sxXSwgZikpKSkKICAgIGNvbG5hbWVzKFJlc3VsdHMpPC1jKCJpdGVyYXRpb24iLCJ0aW1lIiwieSIsInByZWRpY3Rpb24iKQogIH0gZWxzZXsKICAgIHRlbXA8LWFzLmRhdGEuZnJhbWUoY2JpbmQobWF0cml4KHRvU3RyaW5nKGl0ZXIpLCBuY29sID0gMSwgbnJvdyA9IG5yb3coc2FsZXNOZXcpKSxzYWxlc05ldyR0aW1lLHNhbGVzTmV3JHksIGFzLmRhdGEuZnJhbWUobGFwcGx5KHNhbGVzTmV3WzFdLCBmKSkpKQogICAgY29sbmFtZXModGVtcCk8LWMoIml0ZXJhdGlvbiIsInRpbWUiLCJ5IiwicHJlZGljdGlvbiIpCiAgICBSZXN1bHRzPC1yYmluZChSZXN1bHRzLHRlbXApCiAgfQp9CmBgYAo8YnIvPkhhdmluZyB0aGUgcmVzdWx0cyBmb3IgZWFjaCBpdGVyYXRpb24gb2YgYWRkaW5nIGtub3duIHZhbHVlcyB0byBmaXQgdGhlIG1vZGVsLCB3ZSBjYW4gbm93IHBsb3QgdGhlIHByZWRpY3RlZCB2YWx1ZXMgcGVyIGl0ZXJhdGlvbiBhZ2FpbnN0IHRoZSBhY3R1YWwgdmFsdWVzLjxici8+CmBgYHtyfQpwbDUgICAgICAgPC0gZ2dwbG90KFJlc3VsdHMpICsgCiAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcyh4PXRpbWUsIHk9eSksc2l6ZT0zLCBjb2xvdXI9IlJveWFsYmx1ZSIpICsKICAgICAgICAgICAgIGdlb21fbGluZShhZXMoeD10aW1lLCB5PXByZWRpY3Rpb24sY29sPWl0ZXJhdGlvbikpICsKICAgICAgICAgICAgIHlsYWIoIiUgU2FsZXMgVm9sdW1lcyIpICsKICAgICAgICAgICAgIHhsYWIoInRpbWUiKQpwbDUgKyBnZ3RpdGxlKCJQcmVkaWN0ZWQgVmFsdWVzIHBlciBJdGVyYXRpb24gdnMgQWN0dWFsIFZhbHVlcyIpCgpgYGAKPGJyLz5XZSBhbHNvIGNvbXB1dCB0aGUgc3VtIG9mIHJlc2lkdWFsIGVycm9ycyB0byBiZSBhYmxlIHRvIGNvbXBhcmUgdGhlIHJlc3VsdHMgb2YgZWFjaCBtb2RlbDxici8+CmBgYHtyfQpSZXN1bHRzWyJTUiJdPC1hcy5kYXRhLmZyYW1lKChSZXN1bHRzJHktUmVzdWx0cyRwcmVkaWN0aW9uKV4yKQpTU1I8LWFnZ3JlZ2F0ZShSZXN1bHRzJFNSLCBieT1saXN0KGl0ZXJhdGlvbj1SZXN1bHRzJGl0ZXJhdGlvbiksIEZVTj1zdW0pCmdncGxvdChTU1IpICsgZ2VvbV9wb2ludChhZXMoeD1pdGVyYXRpb24sIHk9eCksc2l6ZT0yLCBjb2xvdXI9InJlZCIpICsgZ2d0aXRsZSgiQ29udmVyZ2VuY2Ugb2YgU1NSIHBlciBJdGVyYXRpb24iKQpgYGAKPGJyLz5XZSBzZWUgaG93IGhhdmluZyBtb3JlIGFuZCBtb3JlIGluZm9ybWF0aW9uIG9mIG91ciBuZXcgcHJvZHVjdCBoZWxwIHRoZSBtb2RlbCB0byBoYXZlIGEgYmV0dGVyIGZpdCBvZiB0aGUgZGF0YSBhbmQgdGhlcmVmb3JlIGhhdmUgbG93ZXIgcmVzaWR1YWwgZXJyb3I8YnIvPgo8YnIvPgpXZSBjYW4gcGxvdCB0aGUgZmluYWwgZml0IG9mIHRoZSBtaXhlZCBlZmZlY3QgbW9kZWwgd2l0aCBhbGwgdGhlIGRhdGEgYXZhaWxhYmxlOgpgYGB7cn0KZjwtZml0dGVkKE5ld19tb2RlbF9kdCkKZ2dwbG90KGRhdGE9TmV3X0RhdGEpICsgZ2VvbV9wb2ludChhZXMoeD10aW1lLHk9eSksIGNvbG9yPSJsaWdodHNlYWdyZWVuIiwgc2l6ZT0xKSArIAogIGdlb21fbGluZShhZXMoeD10aW1lLHk9ZiksIGNvbG9yPSJSb3lhbGJsdWUiKSArIGZhY2V0X3dyYXAofmlkLCBuY29sPTYpIApgYGAKV2UgY2FuIHNlZSBob3cgdGhlIG1vZGVsIG1hbmFnZXMgdG8gZ2F0aGVyIHRoZSBlZmZlY3RzIG9mIHRoZSBuZXcgcHJvZHVjdCBhbmQgYXQgdGhlIHNhbWUgdGltZSBkb2VzIG5vdCBkYW1hZ2VzIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIG90aGVyIHByb2R1Y3RzLgo=